package app.server.project.main.service.service.luck;

import app.server.project.core.constants.Def;
import app.server.project.core.constants.TcpCmd;
import app.server.project.core.http.CMSLstResp;
import app.server.project.core.util.MathUtil;
import app.server.project.core.websocket.WSMgr;
import app.server.project.main.service.bo.event.CreateLuckOrderEvent;
import app.server.project.main.service.bo.event.RechargeSuccessEvent;
import app.server.project.main.service.constants.GlobalCfgKey;
import app.server.project.main.service.constants.LuckType;
import app.server.project.main.service.db.dao.cfg.GlobalCfgDao;
import app.server.project.main.service.db.dao.user.UserActiveRewardDao;
import app.server.project.main.service.db.dao.user.UserInfoDao;
import app.server.project.main.service.db.dao.user.UserLuckOrderDao;
import app.server.project.main.service.db.entity.user.UserActiveRewardEntity;
import app.server.project.main.service.db.entity.user.UserLuckOrderEntity;
import app.server.project.main.service.db.mapper.user.RechargeOrderMapper;
import app.server.project.main.service.dto.CommonLuckOrderDto;
import app.server.project.main.service.dto.LuckDto;
import app.server.project.main.service.dto.LuckSumDto;
import app.server.project.main.service.dto.MyLuckOrderDto;
import app.server.project.main.service.dto.race.RaceValDto;
import app.server.project.main.service.dto.solanainfo.SlotResp;
import app.server.project.main.service.service.recharge.CallbackType;
import app.server.project.main.service.service.upay.tron.USDTRateService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.tron.trident.abi.datatypes.generated.Uint256;

import java.math.BigInteger;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.CopyOnWriteArrayList;

@RequiredArgsConstructor
@Service
@Slf4j
public class SOLLuckService {

    private final static long Max = 100;
    private final UserLuckOrderDao userLuckOrderDao;
    private final UserActiveRewardDao userActiveRewardDao;

    private final GlobalCfgDao globalCfgDao;
    private final UserInfoDao userInfoDao;
    private final RechargeOrderMapper rechargeOrderMapper;
    private final SOLRateService solRateService;
    private final USDTRateService usdtRateService;
    private ConcurrentLinkedDeque<UserLuckOrderEntity> deque = new ConcurrentLinkedDeque<>();
    private CopyOnWriteArrayList<UserLuckOrderEntity> successLst = new CopyOnWriteArrayList<>();

    // 生成完整的十六进制字符列表（0-9, a-f）
    public static List<String> generateFullHexList() {
        String[] base58Chars = {
                "1", "2", "3", "4", "5", "6", "7", "8", "9",
                "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
                "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
        };


        return List.of(base58Chars);
    }

    public static void main(String[] arg) {
        var bi = BigInteger.valueOf(17500000);
        System.out.println(new Uint256(bi));
    }

    public CMSLstResp<LuckSumDto> getLuckData() {
        var ret = rechargeOrderMapper.getLuckData();
        ret.forEach(v -> {
            switch (v.getValStr()) {
                case "1": {
                    v.setValStr(MathUtil.getNumFormat(v.getVal(), LuckType.TonRate));
                }
                break;
                case "2": {
                    v.setValStr(MathUtil.getNumFormat(v.getVal(), Def.Ton));
                }
                break;
                default: {
                    v.setValStr(String.valueOf(v.getVal()));
                }
                break;
            }
        });
        return CMSLstResp.of(ret, ret.size());
    }


    @EventListener(CreateLuckOrderEvent.class)
    public void onCreateOrder(CreateLuckOrderEvent event) {
        if (event.getType() != LuckType.SOL) return;
        UserLuckOrderEntity userLuckOrderEntity = UserLuckOrderEntity.of(event.getOrderId());
        userLuckOrderEntity.setChoice(event.getChoice());
        userLuckOrderEntity.setUserId(event.getUserId());

        userLuckOrderEntity.setOrderType(event.getType());
        //刷新缓存
        userLuckOrderDao.updateOrder(userLuckOrderEntity);
    }

    @EventListener(RechargeSuccessEvent.class)
    public void onRechargeSuccess(RechargeSuccessEvent event) {
        if (event.getCallbackType() != CallbackType.SOLLuck) return;
        var order = userLuckOrderDao.getOrder(event.getOrder().getId());
        if (order.getId() == Def.Zero) return;
        if (order.getOrderType() != LuckType.SOL) return;
        if (event.getCreateTime() > Def.Zero) {
            LocalDateTime localDateTime = Instant.ofEpochSecond(event.getCreateTime())
                    .atZone(ZoneId.systemDefault())
                    .toLocalDateTime();
            order.setSuccessTime(localDateTime);
            order.setSuccessUnixTime(event.getCreateTime());
        } else {
            order.setSuccessTime(LocalDateTime.now());
            order.setSuccessUnixTime(System.currentTimeMillis() / 1000);
        }
        //开始进入匹配队列
        deque.add(order);
        var lst = this.userLuckOrderDao.getAll(order.getUserId(), LuckType.SOL, Max);
        lst.removeIf(item -> item.getId() == order.getId());
        lst.add(Def.Zero, order);
        if (lst.size() > Max) {
            var temp = lst.subList((int) Max, lst.size());
            lst.removeAll(temp);
        }
    }

    public void init() {
        var all = userLuckOrderDao.getAllUnSuccess(LuckType.SOL);
        this.deque.addAll(all);
        this.successLst.addAll(this.userLuckOrderDao.getAllLuck(LuckType.SOL, Max));
    }

    /**
     * 15秒查询一次区块,否则触发对方限流
     */


    public List<MyLuckOrderDto> getMyAllSOL(long userId) {
        var lst = this.userLuckOrderDao.getAll(userId, LuckType.SOL, Max);
        return lst.stream().map(item -> {
            var ret = MyLuckOrderDto.of(item, LuckType.TonRate);
            if (ret.getOrder().getSuccessTime() != null) {
                ret.getOrder().setSuccessTimeStr(ret.getOrder().getSuccessTime().plusHours(4));
            }
            return ret;
        }).toList();
    }

    public List<CommonLuckOrderDto> getAllSOLCommon() {
        return successLst.stream().map(item -> {
            if (item.getSuccessTime() != null) {
                item.setSuccessTimeStr(item.getSuccessTime().plusHours(4));
            }
            return CommonLuckOrderDto.of(item, LuckType.TonRate, this.userInfoDao.getUserInfoById(item.getUserId()).getNick());
        }).toList();
    }

    public boolean IsEmpty() {
        return deque.isEmpty();
    }

    public void matchOrder(SlotResp resp) {
        var firstOrder = deque.pollFirst();
        if (firstOrder == null) return;
        int max = 1000;
        //var failLst = new ArrayList<UserLuckOrderEntity>();
        while (firstOrder != null && max > 0) {
            var flag = false;
            try {
                flag = this.matchChoice(firstOrder, resp);
            } catch (Exception e) {
                log.error(" sol matchChoice error", e);
            }
            var allOrders = this.userLuckOrderDao.getAll(firstOrder.getUserId(), LuckType.SOL, Max);
            UserLuckOrderEntity finalFirstOrder = firstOrder;
            allOrders.removeIf(item -> item.getId() == finalFirstOrder.getId());
            allOrders.add(Def.Zero, firstOrder);

            this.successLst.removeIf(item -> item.getId() == finalFirstOrder.getId());
            this.successLst.add(Def.Zero, firstOrder);
            if (flag) {

            } else {
                this.doSmallReward(firstOrder);
            }
            firstOrder = deque.pollFirst();
            max--;
        }
        //失败的订单重新加入队列
        //deque.addAll(failLst);
    }

    private void pushTon(long userId) {
        UserActiveRewardEntity rewardEntity = this.userActiveRewardDao.getInfoById(userId);
        var dto = RaceValDto.ofTon(rewardEntity.getTonVal(), LuckType.TonRate);
        WSMgr.push(userId, TcpCmd.TonVal, dto);
    }

    private void pushReward(long userId, long val, String hash, String choice, int rewardType) {
        var dto = LuckDto.of(String.valueOf(val), String.valueOf(LuckType.TonRate), hash, choice, rewardType);
        WSMgr.push(userId, TcpCmd.SOLReward, dto);
    }

    public boolean matchChoice(UserLuckOrderEntity order, SlotResp resp) {
        order.setHighVal(resp.getSlot());
        order.setHash(resp.getResult().getBlockhash());
        //修改订单状态
        order.setStatus(Def.True);
        var charArray = generateFullHexList();
        //中奖字符串
        String btcStr = order.getHash().substring(order.getHash().length() - 1);
        //中了大奖
        UserActiveRewardEntity rewardEntity = this.userActiveRewardDao.getInfoById(order.getUserId());
        //完全匹配
        if (order.getChoice().equals(btcStr)) {
            //中了头奖
            long reward = 20 * LuckType.TonRate;
            if (this.globalCfgDao.has(GlobalCfgKey.Luck_SOL_First)) {
                reward = this.globalCfgDao.getOneByKey(GlobalCfgKey.Luck_SOL_First).getLong();
            }
            this.reward(order, rewardEntity, reward, 1);
            return true;
        } else {
            //匹配相临的
            var targetIndex = 0;
            for (int i = 0; i < charArray.size(); i++) {
                if (charArray.get(i).equals(btcStr)) {
                    targetIndex = i;
                    break;
                }
            }
            var left = targetIndex - 1;
            if (left < 0) {
                left = charArray.size() - 1;
            }
            var right = targetIndex + 1;
            if (right >= charArray.size()) {
                right = 0;
            }
            var flag = this.reward0(order, rewardEntity, charArray.get(left) + charArray.get(right));
            if (flag) return true;
            //检查2等奖
            //开始检查3等奖
            left = targetIndex - 2;
            left = switch (left) {
                case -1 -> charArray.size() - 1;
                case -2 -> charArray.size() - 2;
                default -> left;
            };
            right = targetIndex + 2;
            right = switch (right) {
                case 58 -> 0;
                case 59 -> 1;
                default -> right;
            };
            flag = this.reward1(order, rewardEntity, charArray.get(left) + charArray.get(right));
            if (flag) return true;
            //开始检查4等奖
            left = targetIndex - 3;
            left = switch (left) {
                case -1 -> charArray.size() - 1;
                case -2 -> charArray.size() - 2;
                case -3 -> charArray.size() - 3;
                default -> left;
            };
            right = targetIndex + 3;
            right = switch (right) {
                case 58 -> 0;
                case 59 -> 1;
                case 60 -> 2;
                default -> right;
            };
            return this.reward2(order, rewardEntity, charArray.get(left) + charArray.get(right));
        }
    }

    private void reward(UserLuckOrderEntity order, UserActiveRewardEntity rewardEntity, long val, int rewardType) {
        rewardEntity.addTonVal(val);
        order.setTonVal(val);
        order.setRewardType(rewardType);
        this.successLst.add(order);
        pushReward(order.getUserId(), val, order.getHash(), order.getChoice(), rewardType);
        pushTon(order.getUserId());
    }

    private boolean reward0(UserLuckOrderEntity order, UserActiveRewardEntity rewardEntity, String ab) {
        if (ab.contains(order.getChoice())) {
            long reward = 10 * LuckType.TonRate;
            if (this.globalCfgDao.has(GlobalCfgKey.Luck_SOL_Second)) {
                reward = this.globalCfgDao.getOneByKey(GlobalCfgKey.Luck_SOL_Second).getLong();
            }
            this.reward(order, rewardEntity, reward, 2);
            return true;
        }
        return false;
    }

    private boolean reward1(UserLuckOrderEntity order, UserActiveRewardEntity rewardEntity, String ab) {
        if (ab.contains(order.getChoice())) {
            long reward = 3 * LuckType.TonRate;
            if (this.globalCfgDao.has(GlobalCfgKey.Luck_SOL_Three)) {
                reward = this.globalCfgDao.getOneByKey(GlobalCfgKey.Luck_SOL_Three).getLong();
            }
            this.reward(order, rewardEntity, reward, 3);
            return true;
        }
        return false;
    }

    private boolean reward2(UserLuckOrderEntity order, UserActiveRewardEntity rewardEntity, String ab) {
        if (ab.contains(order.getChoice())) {
            long reward = LuckType.TonRate;
            if (this.globalCfgDao.has(GlobalCfgKey.Luck_SOL_Four)) {
                reward = this.globalCfgDao.getOneByKey(GlobalCfgKey.Luck_SOL_Four).getLong();
            }
            this.reward(order, rewardEntity, reward, 4);
            return true;
        }
        return false;
    }


    private void doSmallReward(UserLuckOrderEntity order) {
        long smallVal = LuckType.TonRate / 10;
        if (this.globalCfgDao.has(GlobalCfgKey.Luck_SOL_Small)) {
            smallVal = this.globalCfgDao.getOneByKey(GlobalCfgKey.Luck_SOL_Small).getLong();
        }
        UserActiveRewardEntity rewardEntity = this.userActiveRewardDao.getInfoById(order.getUserId());
        rewardEntity.addTonVal(smallVal);
        order.setTonVal(smallVal);
        order.setRewardType(0);
        this.pushTon(order.getUserId());
        WSMgr.push(order.getUserId(), TcpCmd.SOLReward, LuckDto.of(String.valueOf(order.getTonVal()),
                String.valueOf(LuckType.TonRate), order.getHash(), order.getChoice(), 0));
    }
}
